package passwordchecker;

import auxClasses.CharType;
import auxClasses.CharacterType;
import auxClasses.SequenceFinder;
import interfaces.IPasswordChecker;

public class PasswordChecker02 implements IPasswordChecker {
    
    private int score;
    private String result;
    private String password;
    
    public static final String TOO_SHORT = "Too Short";
    public static final String VERY_WEAK = "Very Weak";
    public static final String WEAK = "Weak";
    public static final String MEDIUM = "Medium";
    public static final String STRONG = "Strong";
    public static final String VERY_STRONG = "Very Strong";
    
    private final int MIN_PASSWORD_LENGTH = 8;
    private final int CHARACTER_VALUE = 4;
    private final int UPPERCASE_VALUE = 2;
    private final int LOWERCASE_VALUE = 2;
    private final int DIGIT_VALUE = 4;
    private final int SYMBOL_VALUE = 6;
    private final int SEQUENCE_SIZE = 3;
    
    private final String LETTERS = "abcdefghijlmnopqrstuvwxyz";
    private final String NUMBERS = "01234567890";
    private final String SYMBOLS = ")!@#$%^&*()";
    
    private int passwordLength = 0;
    
    //CHAR HELPER VARIABLES
    private CharType upper = new CharType();
    private CharType lower = new CharType();
    private CharType digit = new CharType(true);
    private CharType symbol = new CharType(true);
    
    //SEQUENCE HELPER VARIABLES
    private SequenceFinder letterSequence = new SequenceFinder(LETTERS);
    private SequenceFinder digitSequence = new SequenceFinder(NUMBERS);
    private SequenceFinder symbolSequence = new SequenceFinder(SYMBOLS);
    
    private int consecutiveType = 0;
    private int specialCharInTheMiddle = 0;
    private double incrementReplica = 0;
    private int characterReplica = 0;
    private int characterUnique = 0;
    private int sequenceTotal = 0;

    public String getResult() {
        return result;
    }

    public int getScore() {
        return score;
    }

    public PasswordChecker02(String password){
        this.password = password;
        
    }
    
    @Override
    public void checkPassword() {
        resetChecker();
        
        int  
                nRequirements = 0, nConsecSymbol = 0,
                nReqChar = 0;
        //int nMultConsecCharType = 0;
        int nAlphasOnly = 0, nNumbersOnly = 0;
        
        //int nMultRepChar = 1, nMultConsecSymbol = 1;
        int nMultMidChar = 2, nMultConsecAlphaUC = 2, nMultConsecAlphaLC = 2, nMultConsecNumber = 2;
        //int nMultRequirements = 2;
        int nMultSeqAlpha = 3, nMultSeqNumber = 3, nMultSeqSymbol = 3;
        //int nReqCharType = 3, nMultAlphaUC = 3, nMultAlphaLC = 3;
        
        
        int nTmpNumber = -1, nTmpSymbol = -1;
        String sAlphaUC = "0", sAlphaLC = "0", sNumber = "0", sSymbol = "0", sMidChar = "0", sRequirements = "0",
                sAlphasOnly = "0", sNumbersOnly = "0", sRepChar = "0", sConsecAlphaUC = "0", sConsecAlphaLC = "0",
                sConsecNumber = "0", sSeqAlpha = "0", sSeqNumber = "0", sSeqSymbol = "0";

        if (isPasswordValid()) {
            passwordLength = password.length();
            getLengthScore();
            
            char[] passwordWithoutSpaces = removeWhiteSpaces().toCharArray();
            verifyPasswordCharByChar(passwordWithoutSpaces);
            
            findSequences();

            System.out.println("nLengthBonus is: + " + score);

            if (upper.getTotalChars() > 0 && upper.getTotalChars() < passwordLength) {
                score = score + (passwordLength - upper.getTotalChars()) * UPPERCASE_VALUE;
                sAlphaUC = "+ " + (passwordLength - upper.getTotalChars()) * UPPERCASE_VALUE;
            }
            if (lower.getTotalChars() > 0 && lower.getTotalChars() < passwordLength) {
                score = score + (passwordLength - lower.getTotalChars()) * LOWERCASE_VALUE;
                sAlphaLC = "+ " + (passwordLength - lower.getTotalChars()) * LOWERCASE_VALUE;
            }
            if (digit.getTotalChars() > 0 && digit.getTotalChars() < passwordLength) {
                score = score + digit.getTotalChars() * DIGIT_VALUE;
                sNumber = "+ " + digit.getTotalChars() * DIGIT_VALUE;
            }
            if (symbol.getTotalChars() > 0) {
                score = score + symbol.getTotalChars() * SYMBOL_VALUE;
                sSymbol = "+ " + symbol.getTotalChars() * SYMBOL_VALUE;
            }
            if (specialCharInTheMiddle > 0) {
                score = score + specialCharInTheMiddle * nMultMidChar;
                sMidChar = "+ " + specialCharInTheMiddle * nMultMidChar;
            }

            System.out.println("nAlphaUCBonus is: " + sAlphaUC);
            System.out.println("nAlphaLCBonus is: " + sAlphaLC);
            System.out.println("nNumberBonus is: " + sNumber);
            System.out.println("nSymbolBonus is: " + sSymbol);
            System.out.println("nMidCharBonus is: " + sMidChar);

            if ((lower.getTotalChars() > 0 || upper.getTotalChars() > 0) && symbol.getTotalChars() == 0 && digit.getTotalChars() == 0) {
                score = score - passwordLength;
                nAlphasOnly = passwordLength;
                sAlphasOnly = "- " + passwordLength;
            }
            if (lower.getTotalChars() == 0 && upper.getTotalChars() == 0 && symbol.getTotalChars() == 0 && digit.getTotalChars() > 0) {
                score = score - passwordLength;
                nNumbersOnly = passwordLength;
                sNumbersOnly = "- " + passwordLength;
            }
            if (characterReplica > 0) {
                score = score - (int) incrementReplica;
                sRepChar = "- " + incrementReplica;
            }
            if (upper.getConsecutiveChars() > 0) {
                score = score - upper.getConsecutiveChars() * nMultConsecAlphaUC;
                sConsecAlphaUC = "- " + upper.getConsecutiveChars() * nMultConsecAlphaUC;
            }
            if (lower.getConsecutiveChars() > 0) {
                score = score - lower.getConsecutiveChars() * nMultConsecAlphaLC;
                sConsecAlphaLC = "- " + lower.getConsecutiveChars() * nMultConsecAlphaLC;
            }
            if (digit.getConsecutiveChars() > 0) {
                score = score - digit.getConsecutiveChars() * nMultConsecNumber;
                sConsecNumber = "- " + digit.getConsecutiveChars() * nMultConsecNumber;
            }
            if (letterSequence.getCount() > 0) {
                score = score - letterSequence.getCount() * nMultSeqAlpha;
                sSeqAlpha = "- " + letterSequence.getCount() * nMultSeqAlpha;
            }
            if (digitSequence.getCount() > 0) {
                score = score - digitSequence.getCount() * nMultSeqNumber;
                sSeqNumber = "- " + digitSequence.getCount() * nMultSeqNumber;
            }
            if (symbolSequence.getCount() > 0) {
                score = score - symbolSequence.getCount() * nMultSeqSymbol;
                sSeqSymbol = "- " + symbolSequence.getCount() * nMultSeqSymbol;
            }

            System.out.println("nAlphasOnlyBonus is: " + sAlphasOnly);
            System.out.println("nNumbersOnlyBonus is: " + sNumbersOnly);
            System.out.println("sRepCharBonus is: " + sRepChar);
            System.out.println("nConsecAlphaUCBonus is: " + sConsecAlphaUC);
            System.out.println("nConsecAlphaLCBonus is: " + sConsecAlphaLC);
            System.out.println("nConsecNumberBonus is: " + sConsecNumber);
            System.out.println("nSeqAlphaBonus is: " + sSeqAlpha);
            System.out.println("nSeqNumberBonus is: " + sSeqNumber);
            System.out.println("nSeqSymbolBonus is: " + sSeqSymbol);

            int[] arrChars = {passwordLength, upper.getTotalChars(), lower.getTotalChars(), digit.getTotalChars(), symbol.getTotalChars()};
            String[] arrCharsIds = {"nLength", "nAlphaUC", "nAlphaLC", "nNumber", "nSymbol"};
            int arrCharsLen = arrChars.length;
            for (int c = 0; c < arrCharsLen; c++) {
                int minVal;
                if (arrCharsIds[c].equals("nLength")) {
                    minVal = MIN_PASSWORD_LENGTH - 1;
                } else {
                    minVal = 0;
                }
                if (arrChars[c] == (minVal + 1)) {
                    nReqChar++;
                } else if (arrChars[c] > (minVal + 1)) {
                    nReqChar++;
                }
            }

            nRequirements = nReqChar;
            int nMinReqChars;
            if (password.length() >= MIN_PASSWORD_LENGTH) {
                nMinReqChars = 3;
            } else {
                nMinReqChars = 4;
            }
            if (nRequirements > nMinReqChars) {
                score = score + nRequirements * 2;
                sRequirements = "+ " + nRequirements * 2;
            }

            System.out.println("nRequirementsBonus is: " + sRequirements);
            
            getFinalResult();

            System.out.println("Strength score is: " + score + "%");
            System.out.println("Strength verdict: " + result);
        } else {
            System.out.println("Strength score is:" + score + "%");
            System.out.println("Strength verdict: " + result);
        }
    }

    private void getFinalResult() {
        int[] maxValues = { 20, 40, 60, 80, 100 };
        String[] results = { VERY_WEAK, WEAK, MEDIUM, STRONG, VERY_STRONG };
        
        if (score > 100) {
            score = 100;
        } else if (score < 0) {
            score = 0;
        }
        
        for(int i = 0; i < maxValues.length; i++){
            if(score <= maxValues[i]){
                result = results[i];
                break;
            }
        }
    }

    private void findSequences() {
        SequenceFinder[] sequences = { letterSequence, digitSequence, symbolSequence }; 
        
        String searchForward;
        String searchReverse;
        String sequence;
        
        for(int i = 0; i < sequences.length; i++){
            
            sequence = new String(sequences[i].getCharSequence());
            
            for (int s = 0; s < sequence.length() - SEQUENCE_SIZE; s++) {
                searchForward = sequence.substring(s, s + SEQUENCE_SIZE);
                searchReverse = new StringBuffer(searchForward).reverse().toString();
                if (password.toLowerCase().indexOf(searchForward) != -1 || password.toLowerCase().indexOf(searchReverse) != -1) {
                    sequences[i].setCount(sequences[i].getCount() + 1);
                    sequenceTotal++;
                }
            }
        }
//        
//        for (int s = 0; s < 22; s++) {
//            String sFwd = LETTERS.substring(s, s + SEQUENCE_SIZE);
//            String sRev = new StringBuffer(sFwd).reverse().toString();
//            if (pwd.toLowerCase().indexOf(sFwd) != -1 || pwd.toLowerCase().indexOf(sRev) != -1) {
//                sequenceLetters++;
//                sequenceTotal++;
//            }
//        }
//
//        for (int s = 0; s < 8; s++) {
//            String sFwd = NUMBERS.substring(s, s + 3);
//            String sRev = new StringBuffer(sFwd).reverse().toString();
//            if (pwd.toLowerCase().indexOf(sFwd) != -1 || pwd.toLowerCase().indexOf(sRev) != -1) {
//                sequenceNumbers++;
//                sequenceTotal++;
//            }
//        }
//
//        for (int s = 0; s < 8; s++) {
//            String sFwd = SYMBOLS.substring(s, s + 3);
//            String sRev = new StringBuffer(sFwd).reverse().toString();
//            if (pwd.toLowerCase().indexOf(sFwd) != -1 || pwd.toLowerCase().indexOf(sRev) != -1) {
//                sequenceSymbols++;
//                sequenceTotal++;
//            }
//        }
    }

    private void getLengthScore() {
        score += passwordLength * CHARACTER_VALUE;
    }

    private void verifyPasswordCharByChar(char[] passwordWithoutSpaces) {
        int passwordLengthWithoutSpaces = passwordWithoutSpaces.length;
        
        CharacterType[] types = { CharacterType.UPPER, CharacterType.LOWER, CharacterType.DIGIT, CharacterType.SYMBOL };
        CharType[] definedTypes = { upper, lower, digit, symbol };
        
        for (int i = 0; i < passwordLengthWithoutSpaces; i++) {
            
            for(int j = 0; j < types.length; j++){
                if(CharType.getType(passwordWithoutSpaces[i]) == types[j]){
                    if(definedTypes[j].isSpecial())
                        setCharType(definedTypes[j], i, passwordLengthWithoutSpaces);
                    else
                        setCharType(definedTypes[j], i);
                }
            }
            
            if (!isCharUniqueInString(passwordWithoutSpaces.toString(), passwordWithoutSpaces[i])) {
                characterReplica++;
                characterUnique = passwordLengthWithoutSpaces - characterReplica;
                incrementReplica = (characterUnique != 0) ? (int) Math.ceil(incrementReplica / characterUnique) : (int) Math.ceil(incrementReplica);
            }
        }
    }

    private boolean isCharUniqueInString(String text, char needle) {
        if(text.indexOf(needle) != text.lastIndexOf(needle))
            return false;
        return true;
    }

    private void setCharType(CharType c, int position, int passwordLength) {
        if (position > 0 && position < (passwordLength - 1)) {
            specialCharInTheMiddle++;
        }
        setCharType(c, position);
    }
    
    private void setCharType(CharType c, int position) {
        if (c.getLetterPosition() != -1) {
            if ((c.getLetterPosition() + 1) == position) {
                c.setConsecutiveChars(c.getConsecutiveChars() + 1);
                consecutiveType++;
            }
        }
        c.setLetterPosition(position);
        c.setTotalChars(c.getTotalChars() + 1);

    }
    
    private boolean isPasswordValid() {
        return password != null && !password.isEmpty();
    }

    private String removeWhiteSpaces() {
        return password.replaceAll("\\s+", "");
    }
    
    private void resetChecker(){
        score = 0;
        result = TOO_SHORT;
        
        upper.reset();
        lower.reset();
        digit.reset();
        symbol.reset();
        
        passwordLength = 0;
        consecutiveType = 0;
        specialCharInTheMiddle = 0;
        incrementReplica = 0;
        characterReplica = 0;
        characterUnique = 0;
        
        letterSequence.setCount(0);
        digitSequence.setCount(0);
        symbolSequence.setCount(0);
        sequenceTotal = 0;
    }
}